//*******************************************************************************
// COPYRIGHT NOTES
// ---------------
// This is a sample for BCGControlBar Library Professional Edition
// Copyright (C) 1998-2016 BCGSoft Ltd.
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions 
// of the accompanying license agreement.
//*******************************************************************************
//
// ChartBoxPlotView.cpp : implementation file
//

#include "stdafx.h"
#include "bcgpchartexample.h"
#include "ChartBoxPlotView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

static int BoxPlotCompare(const void *arg1, const void *arg2)
{
	int nRes = 0;

	if (*(double*)arg1 < *(double*)arg2)
	{
		nRes = -1;
	}
	else if (*(double*)arg2 < *(double*)arg1)
	{
		nRes = 1;
	}

	return nRes;
}

// type:
// 0 - minimum
// 1 - Q1
// 2 - Q3 (median)
// 3 - Q2
// 4 - maximum
static double BoxPlotQuartile(const double* arData, int nCount, int method, int type)
{
    if (nCount == 1)
    {
        return arData[0];
    }

    int nIndex1 = 0;
    int nIndex2 = 0;

    if (type == 0 || type == 4)
    {
        nIndex1 = type == 0 ? 0 : nCount - 1;
        nIndex2 = nIndex1;
    }
    else
    {
        const bool bOddSize = (nCount % 2) != 0;

        if (type == 2)
        {
            nIndex1 = nCount / 2;
            nIndex2 = nIndex1;
            if (!bOddSize)
            {
                nIndex2--;
            }
        }
        else
        {
            int nCount2 = nCount / 2;

            if (method == 1 && bOddSize)
            {
                nCount2 += 1;
            }

            nIndex1 = nCount2 / 2;

            if (type == 3)
            {
                nIndex1 += nCount2;

                if (bOddSize)
                {
                    nIndex1 += method == 0
                                    ? 1
                                    : -1;
                }
            }

            nIndex2 = nIndex1;
            if ((nCount2 % 2) == 0)
            {
                nIndex2 -= 1;
            }
        }
    }

    double result = arData[nIndex1];
    if (nIndex1 != nIndex2)
    {
        result = (arData[nIndex2] + result) / 2.0;
    }

    return result;
}

static void BoxPlotCalculate(double* arData, int nCount, CBCGPChartBoxPlotData& data, BOOL bIsNotch, int method = 0)
{
	data.SetEmpty();

	data.m_dblAverage = 0.0;

	int i = 0;
	for (i = 0; i < nCount; i++)
	{
		data.m_dblAverage += arData[i];
	}

	data.m_dblAverage /= nCount;

	qsort(arData, nCount, sizeof(double), BoxPlotCompare);

	data.m_dblMin = BoxPlotQuartile(arData, nCount, method, 0);
	data.m_dblQ1 = BoxPlotQuartile(arData, nCount, method, 1);
	data.m_dblQ2 = BoxPlotQuartile(arData, nCount, method, 2);
	data.m_dblQ3 = BoxPlotQuartile(arData, nCount, method, 3);
	data.m_dblMax = BoxPlotQuartile(arData, nCount, method, 4);

	data.m_dblNotch = bIsNotch || 1
						? 1.58 * fabs((data.m_dblQ3 - data.m_dblQ1)) / sqrt((double)nCount)
						: DBL_MAX;

	data.m_bIsEmpty = FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CChartBoxPlotView

IMPLEMENT_DYNCREATE(CChartBoxPlotView, CBCGPChartExampleView)

CChartBoxPlotView::CChartBoxPlotView(UINT nID)
	: CBCGPChartExampleView(nID)
	, m_bIsNotched(FALSE)
{
	m_bIsNotched = nID != CChartBoxPlotView::IDD;

	//{{AFX_DATA_INIT(CChartBoxPlotView)
	m_nTransparency = m_bIsNotched ? 75 : 50;
	m_nSizeWhiskers = 50;
	m_nSizeNotch = m_bIsNotched ? 25 : 0;
	//}}AFX_DATA_INIT
}

CChartBoxPlotView::~CChartBoxPlotView()
{
}

void CChartBoxPlotView::DoDataExchange(CDataExchange* pDX)
{
	CBCGPChartExampleView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CChartBoxPlotView)
	DDX_Control(pDX, IDC_TRANSPARENCY, m_wndTransparency);
	DDX_Slider(pDX, IDC_TRANSPARENCY, m_nTransparency);
	DDX_Control(pDX, IDC_WHISKERS, m_wndSizeWhiskers);
	DDX_Slider(pDX, IDC_WHISKERS, m_nSizeWhiskers);

	if (m_bIsNotched)
	{
		DDX_Control(pDX, IDC_NOTCH, m_wndSizeNotch);
		DDX_Slider(pDX, IDC_NOTCH, m_nSizeNotch);
	}

	DDX_Control(pDX, IDC_CHART, m_wndChart);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CChartBoxPlotView, CBCGPChartExampleView)
	//{{AFX_MSG_MAP(CChartBoxPlotView)
	ON_WM_HSCROLL()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChartBoxPlotView diagnostics

#ifdef _DEBUG
void CChartBoxPlotView::AssertValid() const
{
	CBCGPChartExampleView::AssertValid();
}

void CChartBoxPlotView::Dump(CDumpContext& dc) const
{
	CBCGPChartExampleView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CChartBoxPlotView message handlers

void CChartBoxPlotView::OnInitialUpdate() 
{
	CBCGPChartExampleView::OnInitialUpdate();
	
	if (m_bIsReady)
	{
		return;
	}

	m_bIsReady = TRUE;

	m_wndTransparency.SetRange(0, 100, TRUE);
	m_wndSizeWhiskers.SetRange(0, 100, TRUE);

	if (m_bIsNotched)
	{
		m_wndSizeNotch.SetRange(0, 50, TRUE);
	}

	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);

	pChart->SetChartType(BCGPChartBoxPlot);

	pChart->SetChartTitle(_T("Site visitors from US:"));

	if (m_bIsNotched)
	{
		pChart->SwapAxesDirections();
	}

	pChart->CreateSeries(_T("Series 1"));

	if (!m_bIsNotched)
	{
		pChart->CreateSeries(_T("Series 2"));
		pChart->CreateSeries(_T("Series 3"));
	}

	const int nPoints = m_bIsNotched ? 6 : 12;
	const int nData = 16;
	double arData[nData];

	int i = 0;
	int j = 0;
	int k = 0;

	CBCGPChartBoxPlotData data;

	for (i = 0; i < pChart->GetSeriesCount(); i++)
	{
		CBCGPChartBoxPlotSeries* pSeries = DYNAMIC_DOWNCAST(CBCGPChartBoxPlotSeries, pChart->GetSeries(i));

		for (j = 0; j < nPoints; j++)
		{
			const double dblMin = Rand(0.0, 1000.0);
			const double dblMax = Rand(5000.0, 10000.0 - dblMin);

			for (k = 0; k < nData; k++)
			{
				arData[k] = (double)bcg_round(Rand(dblMin, dblMax));
			}

			BoxPlotCalculate(arData, nData, data, m_bIsNotched);

			pSeries->AddData(data);
		}
	}

	UpdateData(FALSE);
	OnUpdateChart();
}

void CChartBoxPlotView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	CBCGPChartExampleView::OnHScroll(nSBCode, nPos, pScrollBar);

	if (pScrollBar != NULL)
	{
		OnUpdateChart();
	}
}

void CChartBoxPlotView::OnUpdateChart()
{
	UpdateData();

	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);

	BOOL bRecalc = FALSE;

	int i = 0;
	for (i = 0; i < pChart->GetSeriesCount(); i++)
	{
		CBCGPChartBoxPlotSeries* pSeries = DYNAMIC_DOWNCAST(CBCGPChartBoxPlotSeries, pChart->GetSeries(i));
		if (pSeries != NULL)
		{
			ASSERT_VALID(pSeries);

			int nSizeNotch = pSeries->GetNotchSizePercent();

			pSeries->SetWhiskersSizePercent(m_nSizeWhiskers);
			pSeries->SetNotchSizePercent(m_bIsNotched ? m_nSizeNotch : 0);

			bRecalc = (nSizeNotch == 0 && pSeries->GetNotchSizePercent() != 0) ||
				(nSizeNotch != 0 && pSeries->GetNotchSizePercent() == 0);
		}
	}

	pChart->SetThemeOpacity(100 - m_nTransparency);

	if (bRecalc)
	{
		pChart->RecalcMinMaxValues();
	}

	pChart->SetDirty(TRUE, TRUE);
}


/////////////////////////////////////////////////////////////////////////////
// CChartBoxPlotNotchView

IMPLEMENT_DYNCREATE(CChartBoxPlotNotchView, CChartBoxPlotView)

CChartBoxPlotNotchView::CChartBoxPlotNotchView()
	: CChartBoxPlotView(CChartBoxPlotNotchView::IDD)
{
	//{{AFX_DATA_INIT(CChartBoxPlotNotchView)
	//}}AFX_DATA_INIT
}

CChartBoxPlotNotchView::~CChartBoxPlotNotchView()
{
}

void CChartBoxPlotNotchView::DoDataExchange(CDataExchange* pDX)
{
	CChartBoxPlotView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CChartBoxPlotNotchView)
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CChartBoxPlotNotchView, CChartBoxPlotView)
	//{{AFX_MSG_MAP(CChartBoxPlotNotchView)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChartBoxPlotNotchView diagnostics

#ifdef _DEBUG
void CChartBoxPlotNotchView::AssertValid() const
{
	CChartBoxPlotView::AssertValid();
}

void CChartBoxPlotNotchView::Dump(CDumpContext& dc) const
{
	CChartBoxPlotView::Dump(dc);
}
#endif //_DEBUG

void CChartBoxPlotNotchView::OnInitialUpdate() 
{
	CChartBoxPlotView::OnInitialUpdate();
}
